package model.primitive;

import java.util.ArrayList;
import java.util.List;

import javax.vecmath.Point2f;
import javax.vecmath.Point3d;
import javax.vecmath.Tuple3d;
import javax.vecmath.Vector3d;

import model.ray.Ray;
import model.shader.Shader;

public class Plane extends Primitive {
	private Point3d refpoint; 		/* punto de referencia del plano */
	private Vector3d normal;
	private Vector3d uAxis;
	private Vector3d vAxis;

	
	public Plane(Shader shader, String name, Tuple3d refpoint, Vector3d normal) {
		super(shader, name);
		this.refpoint = new Point3d(refpoint);
		this.normal = new Vector3d(normal);

		this.uAxis=new Vector3d(this.normal.y, this.normal.z,-1*this.normal.x);
		this.vAxis= new Vector3d();
		this.vAxis.cross(this.uAxis,this.normal);
	}

	public Plane(Point3d p1, Point3d p2, Point3d p3) {
		this(null, null, p1, p2, p3);
	}
	
	public Plane(Shader shader, String name, Point3d p1, Point3d p2, Point3d p3) {
		super(shader, name);

		Vector3d u, v;
		u = new Vector3d();
		v = new Vector3d();
		this.refpoint = p1;
		
		// use given 3 points to create 2 vectors belong to the plane
		// then calculate plane normal using cross operation between 
		// 2 vectors
		u.sub(p2, p1);
		v.sub(p3, p1);

		this.normal = new Vector3d();
		this.normal.cross(u, v);
		
		this.uAxis=new Vector3d(this.normal.y, this.normal.z,-1*this.normal.x);
		this.vAxis= new Vector3d();
		this.vAxis.cross(this.uAxis,this.normal);
	}

	public Point3d getRefPoint() {
		return new Point3d(refpoint);
	}

	public void setRefPoint(Point3d refpoint) {
		this.refpoint = new Point3d(refpoint);
	}

	public Vector3d getNormal() {
		return new Vector3d(normal);
	}

	public void setNormal(Vector3d normal) {
		this.normal = new Vector3d(normal);
	}

	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append("Plane\n");
		sb.append("name: " + getName()).append("\n");
		sb.append("shader: " + getShader()).append("\n");
		sb.append("refPoint: " + this.refpoint).append("\n");
		sb.append("normal: " + this.normal).append("\n");
		return sb.toString();
	}

	@Override
	public List<Point3d> getIntersections(Ray ray) {
		double t, missCondition;
		Vector3d v;

		/* Un plano esta definido por un punto y su normal, por ende un punto x pertenece a un plano si:
		 * (x - p) dot normal  = 0. Donde p es un punto conocido del plano, y n la normal al plano.
		 * Esto significa que el vector formado por (x-p) y el vector normal, forman un angulo de 90, con lo cual, x
		 * pertenece al plano. Basta con reemplazar la ecuacion del rayo en x, para obtener que el punto de interseccion va a estar
		 * dado en:
		 * 
		 *  t = (puntoDelplano - origenRayo) dot normal / (vectorRayo dot normal). Si el denominador
		 *  es 0, es porque no hay interseccion. 
		 */
		
		missCondition = ray.getDirection().dot(this.normal);
		
		if (missCondition == 0) {
			return null; /* Si da 0, es porque el rayo es paralelo al plano o esta conteido por la recta */
		}
		
		v = new Vector3d();
		v.sub(ray.getOrigin(),this.refpoint);
		//v.sub(ray.getOrigin(),this.refpoint);
		
		t = -1 * (v.dot(this.normal) / missCondition);
		
		if (t < 0) {
			return null; // el plano se encuentra atras de la camara
		}
		ArrayList<Point3d> ret = new ArrayList<Point3d>();
		ret.add(ray.getPoint(t));
		return ret;
	}

	@Override
	public void translate(Point3d pos) {
		//should not call this method for plane
		throw new UnsupportedOperationException("can not translate a infinite plane");
	}

	@Override
	public Vector3d getNormal(Point3d pos) {
		return getNormal();
	}

	@Override
	public void rotatex(Double degree) {
		if( degree !=null)
			;
		// TODO Auto-generated method stub
		
	}

	@Override
	public void rotatey(Double degree) {
		if( degree !=null)
			;
		// TODO Auto-generated method stub
		
	}

	@Override
	public void rotatez(Double degree) {
		if( degree !=null)
			;
		// TODO Auto-generated method stub
		
	}

	@Override
	public void scalex(Double sx) {
		if( sx!=null)
			;
		// TODO Auto-generated method stub
		
	}

	@Override
	public void scaley(Double sy) {
		if( sy !=null)
			;
		// TODO Auto-generated method stub
		
	}

	@Override
	public void scalez(Double sz) {
		if( sz !=null)
			;
		// TODO Auto-generated method stub
		
	}

	@Override
	public void scaleu(Double su) {
		if( su !=null)
			;
		// TODO Auto-generated method stub
		
	}

	@Override
	public Point2f getUV(Point3d p) {
		
		Vector3d paux= new Vector3d(p);
		
		// se puede parametrizar el tama�o de la textura multiplicando
		// el producto escalar y luego modulo 1
		
		float u = ((float) paux.dot(this.uAxis))%1;
		float v = ((float) paux.dot(this.vAxis))%1;
		
		return new Point2f(u,v);
	}
	
	
	
}
